define([
    'js/resolve',
    'handsontable',
    'handsontable.zh-CN'
], function(resolve, Handsontable) {
    angular.module('app.handsontable', [])
        .directive('handsontable', handsontableDirective)
        .run(function($templateCache) {
            $templateCache.put('templates/extendbar/handsontable.html',
                '<div class="btn-group">' +
                '  <colorpicker icon="\'fa-font\'" forecolor="\'rgb(51,51,51)\'" backcolor="\'rgb(255,255,0)\'" color-tips="\'字体颜色\'"' +
                '      on-selected="extendbar.fontColor.click"></colorpicker>' +
                '  <div class="btn-group" title="字体大小">' +
                '    <combobox style="width:81px" options="extendbar.fontSize.options"' +
                '        on-selected="extendbar.fontSize.click"></combobox>' +
                '  </div>' +
                '  <button class="btn btn-default" title="粗体" ng-click="extendbar.bold.click()">' +
                '      <i class="fa fa-bold"></i></button>' +
                '  <button class="btn btn-default" title="斜体" ng-click="extendbar.italic.click()">' +
                '      <i class="fa fa-italic"></i></button>' +
                '  <button class="btn btn-default" title="下划线" ng-click="extendbar.underline.click()">' +
                '      <i class="fa fa-underline"></i></button>' +
                '  <button class="btn btn-default" title="删除线" ng-click="extendbar.strikethrough.click()">' +
                '      <i class="fa fa-strikethrough"></i></button>' +
                '  <div class="btn-group" title="对齐方式">' +
                '    <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">' +
                '        <i class="fa fa-align-left"></i> <span class="caret"></span></button>' +
                '    <ul class="dropdown-menu" style="min-width:auto">' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.left.click()">左对齐</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.center.click()">水平居中</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.right.click()">右对齐</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.full.click()">两端对齐</a></li>' +
                '      <li class="divider"></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.top.click()">顶端对齐</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.middle.click()">垂直居中</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.justify.bottom.click()">底端对齐</a></li>' +
                '    </ul>' +
                '  </div>' +
                '  <div class="btn-group" title="表格边框">' +
                '    <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">' +
                '        <i class="fa fa-plus-square-o"></i> <span class="caret"></span></button>' +
                '    <ul class="dropdown-menu" style="min-width:auto">' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.full.click()">所有边框</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.outside.click()">外边框</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.none.click()">无边框</a></li>' +
                '      <li class="divider"></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.bottom.click()">下边框</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.top.click()">上边框</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.left.click()">左边框</a></li>' +
                '      <li><a href="javascript:void(0)" ng-click="extendbar.border.right.click()">右边框</a></li>' +
                '    </ul>' +
                '  </div>' +
                '  <button class="btn btn-default" title="合并单元格" ng-click="extendbar.mergeCell.click()">' +
                '      <i class="fa fa-minus-square-o"></i></button>' +
                '  <button class="btn btn-default" title="清除格式" ng-click="extendbar.clear.click()">' +
                '      <i class="fa fa-eraser"></i></button>' +
                '</div>');
    });

    function handsontableDirective($rootScope, $templateCache, $compile, $timeout, _prettier) {
        return resolve({
            restrict: 'E',
            replace: true,
            template: '<div></div>',
            scope: {
                file: '=',
                context: '=',
                onChanged: '<'
            },
            compile: function() {
                var toolscope = $rootScope.$new(true);
                toolscope.extendbar = {
                    'fontColor': { click: null },
                    'fontSize': { options: _.map([8, 9, 10, 11, 12, 14, 16, 20], function(size) {
                        return {
                            value: size + 'px',
                            label: size + 'px',
                            style: { 'font-size': size + 'px' },
                            selected: size == 14
                        };
                    }), click: null },
                    'bold': { click: null },
                    'italic': { click: null },
                    'underline': { click: null },
                    'strikethrough': { click: null },
                    'justify': {
                        'left': { click: null },
                        'center': { click: null },
                        'right': { click: null },
                        'full': { click: null },
                        'top': { click: null },
                        'middle': { click: null },
                        'bottom': { click: null },
                    },
                    'border': {
                        'full': { click: null },
                        'outside': { click: null },
                        'none': { click: null },
                        'bottom': { click: null },
                        'top': { click: null },
                        'left': { click: null },
                        'right': { click: null }
                    },
                    'mergeCell': { click: null },
                    'clear': { click: null }
                };
                var template = $templateCache.get('templates/extendbar/handsontable.html');
                var extendbar = jQuery($compile(template)(toolscope));
                extendbar.css('display', 'none');
                extendbar.appendTo(jQuery('.extendbar'));

                return {
                    post: function(scope, element) {
                        var context = {
                            init: true, reload: false,
                            cells: {}, rowHeights: {}, colWidths: {},
                            borders: [], borderStyle: { width: 1, color: 'black' }
                        };
                        // editor
                        var editor = new Handsontable(element[0], {
                            language: 'zh-CN',
                            type: 'text',
                            data: [[]],
                            width: '100%',
                            height: '100%',
                            colWidths: 100,
                            rowHeaders: true,
                            colHeaders: true,
                            minRows: 100,
                            minCols: 26,
                            wordWrap: false,
                            autoColumnSize: true,
                            columnSorting: true,
                            dropdownMenu: true,
                            contextMenu: true,
                            trimRows: true,
                            trimWhitespace: true,
                            manualColumnFreeze: true,
                            manualColumnResize: true,
                            manualRowResize: true,
                            manualColumnMove: true,
                            manualRowMove: true,
                            mergeCells: true,
                            fillHandle: true,
                            filters: true,
                            search: true,
                            customBorders: true,
                            outsideClickDeselects: false,
                            renderer: function(instance, TD, row, column, prop, value, cell) {
                                Handsontable.renderers.TextRenderer.apply(this, arguments);
                                if (cell.style) {
                                    for (var style in cell.style) {
                                        TD.style[style] = cell.style[style];
                                    }
                                }
                            },
                            afterChange: function(changes) {
                                $timeout(function() {
                                    if (!context.init && !context.reload) {
                                        scope.file.data = changes;
                                        onFileModified();
                                    }
                                    if (context.init) {
                                        editor.clearUndo();
                                    }
                                    scope.context.navbar['edit']['undo'].enabled = editor.isUndoAvailable();
                                    scope.context.navbar['edit']['redo'].enabled = editor.isRedoAvailable();
                                });
                            },
                            afterSelection: function() {
                                $timeout(function() {
                                    scope.context.navbar['edit']['cut'].enabled = true;
                                    scope.context.navbar['edit']['copy'].enabled = true;
                                });
                            },
                            afterRowResize: function(height, row) {
                                if (context.rowHeights[row] != height) {
                                    context.rowHeights[row] = height;
                                    onFileModified();
                                }
                            },
                            afterColumnResize: function(width, column) {
                                if (width == 100) {
                                    if (context.colWidths[column]) {
                                        delete context.colWidths[column];
                                        onFileModified();
                                    }
                                } else {
                                    if (context.colWidths[column] != width) {
                                        context.colWidths[column] = width;
                                        onFileModified();
                                    }
                                }
                            },
                            afterSetCellMeta: function() {
                                onFileModified();
                            },
                            afterDocumentKeyDown: function(event) {
                                if (event.ctrlKey && event.keyCode == 83) {
                                    event.preventDefault();
                                    if (scope.file.changed) {
                                        scope.context.navbar['file']['save'].click();
                                    }
                                }
                            },
                            licenseKey: 'non-commercial-and-evaluation'
                        });

                        element.bind('mousedown contextmenu', function(event) {
                            // hide other menus
                            jQuery('nav[contextmenu]').removeClass('open');
                            jQuery('.dropdown-menu').parent().removeClass('open');
                        });

                        function onFileModified() {
                            if (!context.init && !context.reload) {
                                $timeout(function() {
                                    scope.file.changed = true;
                                    scope.context.navbar['file']['save'].enabled = scope.file.changed;
                                });
                                if (typeof(scope.onChanged) === 'function') {
                                    scope.onChanged();
                                }
                            }
                        }

                        scope.file.reloader = function(helper, reloaded) {
                            context.reload = true;
                            if (reloaded) {
                                loadAllData(scope.file.data);
                            } else if (scope.file.path) {
                                helper.read(scope.file).then(function(fileObj) {
                                    loadAllData(fileObj.data);
                                });
                            } else if (scope.file.data) {
                                loadAllData(scope.file.data);
                            } else {
                                context.init = false;
                                context.reload = false;
                            }
                            $timeout(function() {
                                editor.refreshDimensions();
                            });

                            function loadAllData(data) {
                                if (data) {
                                    var source = _.extend({
                                        rows: 0, cols: 0, cells: {}, mergeCells: {}, rowHeights: {}, colWidths: {}
                                    }, JSON.parse(data));
                                    context.rowHeights = source.rowHeights;
                                    context.colWidths = source.colWidths;

                                    var dataArr = [];
                                    for (var row = 0; row <= source.rows; row++) {
                                        var cells = [];
                                        for (var col = 0; col <= source.cols; col++) {
                                            var cell = source.cells[row + ',' + col];
                                            cells.push(cell ? cell.data : null);
                                        }
                                        dataArr.push(cells);
                                    }
                                    editor.loadData(dataArr);

                                    var borders = [];
                                    for (var row = 0; row <= source.rows; row++) {
                                        for (var col = 0; col <= source.cols; col++) {
                                            var cell = source.cells[row + ',' + col];
                                            if (cell) {
                                                if (cell.css || cell.style) {
                                                    editor.setCellMetaObject(row, col, {
                                                        className: cell.css, style: cell.style
                                                    });
                                                }
                                                if (cell.border) {
                                                    var border = {
                                                        range: {
                                                            from: { row: row, col: col },
                                                            to: { row: row, col: col }
                                                        }
                                                    };
                                                    _.each(cell.border, function(dir) {
                                                        border[dir] = context.borderStyle;
                                                    })
                                                    borders.push(border);
                                                }
                                            }
                                        }
                                    }
                                    editor.render();

                                    var mergeCells = [];
                                    _.each(source.mergeCells, function(span, cell) {
                                        var pos = JSON.parse('[' + cell + ']')
                                        mergeCells.push({
                                            row: pos[0],
                                            col: pos[1],
                                            rowspan: span.rows,
                                            colspan: span.cols
                                        });
                                    });

                                    editor.updateSettings({
                                        rowHeights: function(row) {
                                            return context.rowHeights[row];
                                        },
                                        colWidths: function(column) {
                                            var width = context.colWidths[column];
                                            return width ? width: 100;
                                        },
                                        customBorders: borders.length > 0 ? borders : true,
                                        mergeCells: mergeCells.length > 0 ? mergeCells : true
                                    }/*, context.init*/);
                                } else {
                                    context.rowHeights = {};
                                    context.colWidths = {};
                                }
                                $timeout(function() {
                                    context.init = false;
                                    context.reload = false;
                                    $timeout(function() {
                                        scope.file.changed = false;
                                        scope.context.navbar['file']['save'].enabled = false;
                                    });
                                }, 50);
                            }
                        }

                        scope.file.extractor = function() {
                            var data = {
                                rows: 0, cols: 0, cells: {},
                                mergeCells: {},
                                rowHeights: context.rowHeights,
                                colWidths: context.colWidths
                            };

                            _.each(editor.getData(), function(cells, row) {
                                _.each(cells, function(cell, column) {
                                    if (cell) {
                                        var cellKey = row + ',' + column;
                                        var cellData = {};
                                        cellData.data = cell;
                                        var meta = editor.getCellMeta(row, column);
                                        if (meta.className) {
                                            cellData.css = meta.className.trim();
                                        }
                                        if (meta.style) {
                                            cellData.style = meta.style;
                                        }
                                        data.cells[cellKey] = cellData;
                                        data.rows = Math.max(row, data.rows);
                                        data.cols = Math.max(column, data.cols);
                                    }
                                });
                            });

                            var borders = editor.getPlugin('CustomBorders').getBorders();
                            _.each(borders, function(border) {
                                var cellKey = border.row + ',' + border.col;
                                var cellData = data.cells[cellKey] || {};
                                cellData.border = [];
                                _.each(['top', 'right', 'bottom', 'left'], function(dir) {
                                    if (!border[dir].hide) {
                                        cellData.border.push(dir);
                                    }
                                });
                                data.cells[cellKey] = cellData;
                                data.rows = Math.max(border.row, data.rows);
                                data.cols = Math.max(border.col, data.cols);
                            });

                            var mergeCells = editor.getPlugin('MergeCells').mergedCellsCollection.mergedCells;
                            _.each(mergeCells, function(cell) {
                                if (!cell.removed) {
                                    data.mergeCells[cell.row + ',' + cell.col] = {
                                        rows: cell.rowspan, cols: cell.colspan
                                    };
                                }
                            });

                            scope.file.data = _prettier.format(JSON.stringify(data, function(key, value) {
                                return value == '' || value == null ? undefined : value;
                            }), {
                                parser: 'json',
                                printWidth: 80,
                            });
                        }

                        scope.file.renderer = function(helper) {
                            // editor
                            if (!scope.file.opened) {
                                scope.file.opened = true;
                                scope.file.reloader(helper);
                            }

                            // extendbar
                            extendbar.css('display', '');
                            toolscope.extendbar['fontColor'].click = function(forecolor, backcolor) {
                                renderRangeStyle(function(style) {
                                    style['color'] = forecolor;
                                    style['backgroundColor'] = backcolor;
                                });
                            };
                            toolscope.extendbar['fontSize'].click = function(size) {
                                renderRangeStyle(function(style) {
                                    style['fontSize'] = size;
                                });
                            };
                            toolscope.extendbar['bold'].click = function() {
                                renderRangeFont('htBold');
                            };
                            toolscope.extendbar['italic'].click = function() {
                                renderRangeFont('htItalic');
                            };
                            toolscope.extendbar['underline'].click = function() {
                                renderRangeFont('htUnderline');
                            };
                            toolscope.extendbar['strikethrough'].click = function() {
                                renderRangeFont('htStrikethrough');
                            };
                            toolscope.extendbar['justify']['left'].click = function() {
                                renderRangeJustify('htLeft');
                            };
                            toolscope.extendbar['justify']['center'].click = function() {
                                renderRangeJustify('htCenter');
                            };
                            toolscope.extendbar['justify']['right'].click = function() {
                                renderRangeJustify('htRight');
                            };
                            toolscope.extendbar['justify']['full'].click = function() {
                                renderRangeJustify('htJustify');
                            };
                            toolscope.extendbar['justify']['top'].click = function() {
                                renderRangeJustify('htTop');
                            };
                            toolscope.extendbar['justify']['middle'].click = function() {
                                renderRangeJustify('htMiddle');
                            };
                            toolscope.extendbar['justify']['bottom'].click = function() {
                                renderRangeJustify('htBottom');
                            };
                            toolscope.extendbar['mergeCell'].click = function() {
                                var range = editor.getSelectedRangeLast();
                                if (range) {
                                    editor.getPlugin('MergeCells').toggleMergeOnSelection();
                                }
                            };
                            toolscope.extendbar['border']['full'].click = function() {
                                renderRangeBorder({
                                    top: context.borderStyle,
                                    right: context.borderStyle,
                                    bottom: context.borderStyle,
                                    left: context.borderStyle
                                });
                            };
                            toolscope.extendbar['border']['outside'].click = function() {
                                toolscope.extendbar['border']['bottom'].click();
                                toolscope.extendbar['border']['top'].click();
                                toolscope.extendbar['border']['left'].click();
                                toolscope.extendbar['border']['right'].click();
                            };
                            toolscope.extendbar['border']['none'].click = function() {
                                renderRangeBorder(null);
                            };
                            toolscope.extendbar['border']['bottom'].click = function() {
                                renderRangeBorder({
                                    bottom: context.borderStyle
                                }, function(range) {
                                    return [range.to.row, range.from.col, range.to.row, range.to.col];
                                });
                            };
                            toolscope.extendbar['border']['top'].click = function() {
                                renderRangeBorder({
                                    top: context.borderStyle
                                }, function(range) {
                                    return [range.from.row, range.from.col, range.from.row, range.to.col];
                                });
                            };
                            toolscope.extendbar['border']['left'].click = function() {
                                renderRangeBorder({
                                    left: context.borderStyle
                                }, function(range) {
                                    return [range.from.row, range.from.col, range.to.row, range.from.col];
                                });
                            };
                            toolscope.extendbar['border']['right'].click = function() {
                                renderRangeBorder({
                                    right: context.borderStyle
                                }, function(range) {
                                    return [range.from.row, range.to.col, range.to.row, range.to.col];
                                });
                            };
                            toolscope.extendbar['clear'].click = function() {
                                renderRange(function(meta) {
                                    return { style: null, className: '' };
                                }, 'clear');
                            };

                            // navbar
                            scope.context.navbar['file']['save'].enabled = scope.file.changed;
                            scope.context.navbar['file']['save'].click = function() {
                                helper.save(scope.file);
                            };
                            scope.context.navbar['edit']['undo'].enabled = editor.isUndoAvailable();
                            scope.context.navbar['edit']['undo'].click = function() {
                                editor.undo();
                            };
                            scope.context.navbar['edit']['redo'].enabled = editor.isRedoAvailable();
                            scope.context.navbar['edit']['redo'].click = function() {
                                editor.redo();
                            };
                            var hasSelected = !_.isEmpty(editor.getSelectedRange());
                            scope.context.navbar['edit']['cut'].enabled = hasSelected;
                            scope.context.navbar['edit']['cut'].click = function() {
                                document.execCommand('cut');
                            };
                            scope.context.navbar['edit']['copy'].enabled = hasSelected;
                            scope.context.navbar['edit']['copy'].click = function() {
                                document.execCommand('copy');
                            };
                            scope.context.navbar['edit']['paste'].click = function() {
                                // TODO
                            };
                            scope.context.navbar['edit']['selectAll'].enabled = true;
                            scope.context.navbar['edit']['selectAll'].click = function() {
                                editor.selectAll(false);
                            }
                            scope.context.navbar['edit']['delete'].enabled = true;
                            scope.context.navbar['edit']['delete'].click = function() {
                                renderRange(function(meta) {
                                    return { style: null, className: '' };
                                }, 'delete');
                            };
                            scope.context.navbar['edit']['find'].enabled = false;
                            scope.context.navbar['edit']['format'].enabled = false;

                            // statusbar
                            scope.context.statusbar.link = scope.file.path;
                            scope.context.statusbar.statuses = ['可写'];
                        };

                        function renderRangeStyle(callback) {
                            renderRange(function(meta, cell) {
                                var style = meta.style || {};
                                callback(style, cell);
                                return {
                                    style: style
                                };
                            });
                        }

                        function renderRangeFont(css) {
                            renderRange(function(meta, cell) {
                                var className = meta.className || '';
                                if (className.indexOf(css) == -1) {
                                    className += ' ' + css;
                                } else if (cell) {
                                    className = className.replace(css, '');
                                }
                                return {
                                    className: className
                                };
                            });
                        }

                        function renderRangeJustify(css) {
                            renderRange(function(meta, cell) {
                                var className = meta.className || '';
                                if (className.indexOf(css) == -1) {
                                    var hr = /htLeft|htCenter|htRight|htJustify/g,
                                        vr = /htTop|htMiddle|htBottom/g;
                                    if (hr.test(css)) {
                                        className = className.replace(hr, '');
                                    } else if (vr.test(css)) {
                                        className = className.replace(vr, '');
                                    }
                                    className += ' ' + css;
                                } else if (cell) {
                                    className = className.replace(css, '');
                                }
                                return {
                                    className: className
                                };
                            });
                        }

                        function renderRangeBorder(border, callback) {
                            var ranges = editor.getSelectedRange();
                            if (ranges) {
                                if (typeof(callback) === 'function') {
                                    ranges = _.map(ranges, callback);
                                }
                                if (border) {
                                    editor.getPlugin('CustomBorders').setBorders(ranges, border);
                                } else {
                                    editor.getPlugin('CustomBorders').clearBorders(ranges);
                                }
                            }
                        }

                        function renderRange(callback, action) {
                            var ranges = editor.getSelectedRange();
                            if (ranges) {
                                _.each(ranges, function(range) {
                                    var from = range.from;
                                    var to = range.to;
                                    var cell = from.row == to.row && from.col == to.col;
                                    for (var row = from.row; row <= to.row; row++) {
                                        for (var column = from.col; column <= to.col; column++) {
                                            var meta = editor.getCellMeta(row, column);
                                            editor.setCellMetaObject(row, column, callback(meta, cell));
                                            if (action == 'delete') {
                                                editor.setDataAtCell(row, column, null);
                                            }
                                        }
                                    }
                                });
                                if (action == 'clear' || action == 'delete') {
                                    editor.getPlugin('CustomBorders').clearBorders(ranges);
                                    //editor.getPlugin('MergeCells').unmergeSelection();
                                }
                                editor.render();
                            }
                        }

                        scope.$on('@resize.all', function(event) {
                            $timeout(function() {
                                editor.refreshDimensions();
                            });
                        });
        
                        // destroy
                        scope.$on('$destroy', function() {
                            element.unbind();
                            editor.destroy();
                        });
                    }
                };
            }
        });
    }
});
